package transport

import (
	"errors"
	"fmt"
	"net"
	"sync"
)

type UDPClient struct {
	Statistic
	host       string
	port       uint16
	localport  uint16
	conn       Connection
	readChan   chan *Packet
	writeChan  chan *Packet
	done       chan struct{}
	remoteAddr net.Addr
	localAddr  net.Addr
	wait       sync.WaitGroup
}

func NewUDPClient(host string, port uint16, locaddr string) (IClient, error) {
	laddr, err := net.ResolveUDPAddr("udp", locaddr)
	if err != nil {
		fmt.Println("客户端地址解析失败: ", err)
		return nil, err
	}

	addrStr := fmt.Sprintf("%s:%d", host, port)
	addr, err := net.ResolveUDPAddr("udp", addrStr)
	if err != nil {
		fmt.Println("服务端地址解析失败: ", err)
		return nil, err
	}
	return &UDPClient{
		host:       host,
		port:       port,
		localport:  uint16(laddr.Port),
		readChan:   make(chan *Packet, 50),
		writeChan:  make(chan *Packet, 50),
		done:       make(chan struct{}),
		localAddr:  laddr,
		remoteAddr: addr,
		wait:       sync.WaitGroup{},
	}, nil
}

func (c *UDPClient) IsReliable() bool {
	return false
}

func (c *UDPClient) Name() string {
	return fmt.Sprintf("udp client to:%s", fmt.Sprintf("%s:%d", c.host, c.port))
}
func (c *UDPClient) LocalAddr() net.Addr {
	return c.localAddr
}

func (c *UDPClient) RemoteAddr() net.Addr {
	return c.remoteAddr
}

func (c *UDPClient) StartAndWait() error {
	if c.localAddr == nil || c.remoteAddr == nil {
		fmt.Println("服务端地址解析失败: ", c.localAddr, c.remoteAddr)
		return errors.New("地址解析失败")
	}

	conn, err := net.DialUDP("udp", c.localAddr.(*net.UDPAddr), c.remoteAddr.(*net.UDPAddr))
	if err != nil {
		fmt.Println("Can't dial: ", err)
		return err
	}

	udpConnection := newUDPConnection(conn)
	c.conn = udpConnection
	c.done = make(chan struct{})
	fmt.Println("udp client remote addr:", conn.RemoteAddr().String())
	fmt.Println("udp client local addr:", conn.LocalAddr().String())
	go func() {
		defer conn.Close()
		for {
			select {
			case <-c.done:
				return
			default:
				buf := make([]byte, 2048)
				n, _, err := conn.ReadFromUDP(buf)
				if err != nil {
					fmt.Println("failed to read UDP msg because of ", err.Error())
					return
				}

				c.readChan <- &Packet{
					Addr: c.remoteAddr,
					Data: buf[:n],
				}

			}

		}
	}()

	return nil
}

func (c *UDPClient) ReadPacketChan() <-chan *Packet {
	return c.readChan
}

func (c *UDPClient) WritePacket(packet *Packet) {
	c.writeChan <- packet
}

func (c *UDPClient) Close() error {
	close(c.done)
	return c.conn.Close()
}

// 外部定期调用此接口，实现心跳
func (c *UDPClient) Heartbeat(p *Packet) {
	if p == nil {
		p = &Packet{
			Data: []byte("ping"),
		}
	}
	c.WritePacket(p)
}

func (c *UDPClient) Conn(addr string) *Connection {
	return &c.conn
}
